import * as THREE from 'three';
// Orbit controls（轨道控制器）可以使得相机围绕目标进行轨道运动
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

/**
 * 为了真正能够让你的场景借助 three.js 来进行显示，
 * 需要以下几个对象：场景（scene）、相机（camera）和渲染器（renderer），
 * 就能透过摄像机渲染出场景。
 * */
const scene = new THREE.Scene();
// scene.background = new THREE.Color('gray')
/**
 * PerspectiveCamera（透视摄像机）
 * 第一个参数是视野角度（FOV）
 *    视野角度就是无论在什么时候，你所能在显示器上看到的场景的范围，它的单位是角度(与弧度区分开)。
 * 第二个参数是长宽比（aspect ratio）
 *    用一个物体的宽除以它的高的值。比如说，当你在一个宽屏电视上播放老电影时，可以看到图像仿佛是被压扁的。
 * 接下来的两个参数是近截面（near）和远截面（far）
 *    当物体某些部分比摄像机的远截面远或者比近截面近的时候，该这些部分将不会被渲染到场景中。
 *    或许现在你不用担心这个值的影响，但未来为了获得更好的渲染性能，你将可以在你的应用程序里去设置它。
 */
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
// const helper = new THREE.CameraHelper( camera );
// scene.add( helper );
camera.position.set(0, 100, 100)
camera.lookAt(0,0,0)


/**
 * 接下来是渲染器。
 *    在这里用到的 WebGLRenderer 渲染器之外，Three.js 同时提供了其他几种渲染器，当用户所使用的浏览器过于老旧，或者由于其他原因不支持 WebGL 时，可以使用这几种渲染器进行降级。
 */
const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector('#canvas'), //指定渲染的容器，不添加则会自动生成
  antialias: true, //抗锯齿 像素点
});
renderer.shadowMap.enabled = true


/**
 *    还需要在我们的应用程序里设置一个渲染器的尺寸。比如说，我们可以使用所需要的渲染区域的宽高，来让渲染器渲染出的场景填充满我们的应用程序。
 *    因此，我们可以将渲染器宽高设置为浏览器窗口宽高。
 *    对于性能比较敏感的应用程序来说，可以使用 setSize 传入一个较小的值，例如 window.innerWidth/2 和 window.innerHeight/2，这将使得应用程序在渲染时，以一半的长宽尺寸渲染场景。
 *    如果希望保持应用程序的尺寸，但是以较低的分辨率来渲染，可在调用 setSize 时，将 updateStyle（第三个参数）设为 false。
 *    例如，假设你的 <canvas> 标签现在已经具有了 100% 的宽和高，调用 setSize(window.innerWidth/2, window.innerHeight/2, false) 将使得你的应用程序以四分之一的大小来进行渲染。
 *    最后一步很重要，我们将 renderer（渲染器）的dom元素（renderer.domElement）添加到我们的 HTML 文档中。这就是渲染器用来显示场景给我们看的 <canvas> 元素。
 */
// renderer.setSize( window.innerWidth, window.innerHeight ); //设置渲染器的大小
/**
 *    最后一步很重要，我们将 renderer（渲染器）的dom元素（renderer.domElement）添加到我们的 HTML 文档中。这就是渲染器用来显示场景给我们看的 <canvas> 元素。
 */
// document.body.appendChild( renderer.domElement ); //则需要添加至页面

// 创建坐标网格辅助线
const size = 100;
const divisions = 10;

const gridHelper = new THREE.GridHelper( size, divisions, 0xff0000 );

// 标准网格材质受光源影响

// 平行光是沿着特定方向发射的光   常常用平行光来模拟太阳光的效果
const directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
directionalLight.position.set(5, 80, 0)
directionalLight.castShadow = true  // 光源开启允许生成阴影

// 使阴影更清晰  设置分辨率
directionalLight.shadow.mapSize.width = 2048; // default 512
directionalLight.shadow.mapSize.height = 2048; // default 512
// directionalLight.shadow.camera.near = 0.5; // default
// directionalLight.shadow.camera.far = 500; // default

// scene.add(directionalLight)
// Create a helper for the shadow camera
const cameraHelper = new THREE.CameraHelper( directionalLight.shadow.camera );
// scene.add( cameraHelper );
// 模拟场景中平行光                                            光源    平面尺寸默认1  光源颜色
const directionalLightHelp = new THREE.DirectionalLightHelper(directionalLight, 15, 0xff0000)
// scene.add(directionalLightHelp)


// 点光源 从一个点向各个方向发射的光源 可以投射阴影   
                  // 光颜色  强度 光源照射的最大距离（默认0无限远） 沿着光照距离的衰退量(默认2) 
const pointLight = new THREE.PointLight( 0xff0000, 1, 100, 0.0 );
// pointLight.position.set(2, 35, 30)
// scene.add(pointLight)
const ponitLightHelper = new THREE.PointLightHelper(pointLight, 5)
// scene.add(ponitLightHelper)


// 聚光灯  （颜色   光强度  距离   照射范围  半影衰减百分比  光照距离衰减量） 
const spotLight = new THREE.SpotLight(0x00ff00, 1000)
// spotLight.map = new THREE.TextureLoader().load('./image/square.jpg');
spotLight.position.set(10, 65, 0)
// // 照射范围
// spotLight.angle = Math.PI / 10
// // 边界
// spotLight.penumbra = 0.2

spotLight.castShadow = true  // 光源开启允许生成阴影
scene.add(spotLight)
const spotLightHelper = new THREE.SpotLightHelper(spotLight, '#ccc')
// scene.add(spotLightHelper)

// 使用纹理加载器二  使用循环渲染就无需再回调中重新渲染
const texture = new THREE.TextureLoader().load('./image/pandas.jpg')
// 创建结构  几何体--平面                
const plane = new THREE.Mesh(
  new THREE.PlaneGeometry(60,60),
  new THREE.MeshStandardMaterial()
)
plane.receiveShadow = true // 光源照射的物体产生的投影 投射到接收的物体上 开启允许
plane.rotation.x = -Math.PI / 2

// 创建结构  几何体--平面                
const cube = new THREE.Mesh(
  new THREE.BoxGeometry(10,10,10),
  new THREE.MeshStandardMaterial()
)
cube.castShadow = true //被光源照射的物体允许产生阴影
cube.position.set(0,35,0)

// 创建结构  几何体--球体                        
const sphere = new THREE.Mesh( 
  new THREE.SphereGeometry(10,32,32), 
  new THREE.MeshStandardMaterial()
);
sphere.castShadow = true //被光源照射的物体允许产生阴影
sphere.position.set(-30,35,0)

// 创建结构  几何体--圆环纽结            
const torus = new THREE.Mesh(
  new THREE.TorusGeometry(10,3),
  new THREE.MeshStandardMaterial()
)
torus.castShadow = true //被光源照射的物体允许产生阴影
torus.position.set(30,35,0)

scene.add(plane, cube, sphere, torus)



// 模拟3个坐标轴的对象.
// 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
const axesHelper = new THREE.AxesHelper(150);
// 设置x,y,z轴的颜色
axesHelper.setColors(new THREE.Color(0xff0000), new THREE.Color(0x00ff00), new THREE.Color('pink'))


scene.add( gridHelper, axesHelper );

// 控制器的使用
// 控制器实例化  参数：相机  绑定事件的元素
const controls = new OrbitControls( camera, renderer.domElement );

//controls.update() must be called after any manual changes to the camera's transform
controls.enableDamping = true //启用惯性


controls.addEventListener('change', () => {
  renderer.render( scene, camera )
  console.log(camera.position);
  
})

renderer.render( scene, camera );  //render  执行一次渲染一次

/**
 * 进行真正的渲染
 * 需要使用一个被叫做“渲染循环”（render loop）或者“动画循环”（animate loop）的东西。
 */
let angle = 0 
function animate() {
	requestAnimationFrame( animate );
  angle += 0.01
  spotLight.position.x = 10 * Math.cos(angle)
  spotLight.position.z = 10 * Math.sin(angle)
  controls.update();
	renderer.render( scene, camera );  //render  执行一次渲染一次
}
animate();
